package idavollr

import (
	"errors"
	"log"

	"apiote.xyz/p/asgard/jotunheim"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-imap/client"
)

type AbstractMailbox interface {
	MailboxName() string
	ImapAddress() string
	ImapUsername() string
	ImapPassword() string
	SetClient(*client.Client)
	Client() *client.Client
	Config() jotunheim.Config
	SetMailbox(*imap.MailboxStatus)
	GetMailbox() *imap.MailboxStatus
	SetSection(*imap.BodySectionName)
	Section() *imap.BodySectionName
	Messages() chan *imap.Message
	Done() chan error

	SetupChannels()
}

type Mailbox struct {
	MboxName string
	ImapAdr  string
	ImapUser string
	ImapPass string
	Conf     jotunheim.Config

	cli  *client.Client
	mbox *imap.MailboxStatus
	sect *imap.BodySectionName
	msgs chan *imap.Message
	dn   chan error
}

func (m Mailbox) MailboxName() string {
	return m.MboxName
}

func (m Mailbox) ImapAddress() string {
	return m.ImapAdr
}

func (m Mailbox) ImapUsername() string {
	return m.ImapUser
}

func (m Mailbox) ImapPassword() string {
	return m.ImapPass
}

func (m *Mailbox) SetClient(c *client.Client) {
	m.cli = c
}

func (m Mailbox) Client() *client.Client {
	return m.cli
}

func (m Mailbox) Config() jotunheim.Config {
	return m.Conf
}

func (m *Mailbox) SetMailbox(mbox *imap.MailboxStatus) {
	m.mbox = mbox
}

func (m Mailbox) GetMailbox() *imap.MailboxStatus {
	return m.mbox
}

func (m *Mailbox) SetSection(sect *imap.BodySectionName) {
	m.sect = sect
}

func (m Mailbox) Section() *imap.BodySectionName {
	return m.sect
}

func (m Mailbox) Messages() chan *imap.Message {
	return m.msgs
}

func (m Mailbox) Done() chan error {
	return m.dn
}

func (m *Mailbox) SetupChannels() {
	m.msgs = make(chan *imap.Message, 10)
	m.dn = make(chan error, 1)
}

func Connect(m AbstractMailbox) (AbstractMailbox, error) {
	c, err := client.DialTLS(m.ImapAddress(), nil)
	m.SetClient(c)
	return m, err
}

func ConnectInsecure(m AbstractMailbox) (AbstractMailbox, error) {
	c, err := client.Dial(m.ImapAddress())
	m.SetClient(c)
	return m, err
}

func Login(m AbstractMailbox) error {
	return m.Client().Login(m.ImapUsername(), m.ImapPassword())
}

func SelectInbox(m AbstractMailbox) (AbstractMailbox, error) {
	mbox, err := m.Client().Select(m.MailboxName(), false)
	m.SetMailbox(mbox)
	return m, err
}

func CheckEmptyBox(m AbstractMailbox) error {
	if m.GetMailbox().Messages == 0 {
		return EmptyBoxError{}
	}
	return nil
}

func FetchMessages(m AbstractMailbox) AbstractMailbox {
	from := uint32(1)
	to := m.GetMailbox().Messages
	seqset := new(imap.SeqSet)
	seqset.AddRange(from, to)

	m.SetSection(&imap.BodySectionName{})
	items := []imap.FetchItem{imap.FetchEnvelope, m.Section().FetchItem(), imap.FetchUid}
	go func() {
		m.Done() <- m.Client().Fetch(seqset, items, m.Messages())
	}()
	return m
}

func CheckFetchError(m AbstractMailbox) error {
	return <-m.Done()
}

func Expunge(m AbstractMailbox) error {
	return m.Client().Expunge(nil)
}

func IgnoreEmptyBox(m AbstractMailbox, e error) (AbstractMailbox, error) {
	var emptyBoxError EmptyBoxError
	if errors.As(e, &emptyBoxError) {
		log.Println("Mailbox is empty")
		return m, nil
	}
	return m, e
}

func Disconnect(m AbstractMailbox, e error) (AbstractMailbox, error) {
	if m.Client() != nil {
		m.Client().Logout()
		m.Client().Close()
	}
	return m, e
}
